package com.miao.server;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;

import java.net.URI;
import java.util.concurrent.CountDownLatch;

/**
 * @Author <a href="https://gitee.com/a-tom-is-cry">Xing</a>
 * @Date 2024/9/8 18:46
 * @Description
 */
public class WebsocketClient {

    static final CountDownLatch latch = new CountDownLatch(1); // 用于阻塞主线程，直到握手完成

    public Channel test(String url) throws Exception {
        Channel dest = init(url); // 获取目标通道
        latch.await(); // 等待握手完成
        dest.writeAndFlush(new TextWebSocketFrame("我就是要发送的消息2")); // 发送消息
        return dest;
    }

    public Channel init(String url) throws Exception {
        final URI webSocketURL = new URI(url); // 目标 WebSocket 地址

        EventLoopGroup group = new NioEventLoopGroup(); // 创建事件循环组
        Bootstrap boot = new Bootstrap(); // 创建引导程序
        boot.option(ChannelOption.SO_KEEPALIVE, true)
                .option(ChannelOption.TCP_NODELAY, true)
                .group(group)
                .handler(new LoggingHandler(LogLevel.INFO)) // 添加日志处理器，用于打印日志信息
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    protected void initChannel(SocketChannel sc) throws Exception {
                        ChannelPipeline pipeline = sc.pipeline();
                        pipeline.addLast(new HttpClientCodec()); // HTTP 客户端编解码器，用于处理 HTTP 请求和响应
                        pipeline.addLast(new ChunkedWriteHandler()); // 支持大数据流写入
                        pipeline.addLast(new HttpObjectAggregator(64 * 1024)); // 聚合 HTTP 消息，将多个消息合并成一个完整的 FullHttpRequest 或 FullHttpResponse
                        // WebSocket 客户端协议处理器，用于处理 WebSocket 握手和帧的编解码
                        pipeline.addLast(new WebSocketClientProtocolHandler(
                                WebSocketClientHandshakerFactory.newHandshaker(webSocketURL, WebSocketVersion.V13, null, false, new DefaultHttpHeaders())));
                        pipeline.addLast(new WebSocketClientHandler());

                    }
                });

        ChannelFuture cf = boot.connect(webSocketURL.getHost(), webSocketURL.getPort()).sync(); // 连接到目标 WebSocket 服务器

        return cf.channel(); // 返回通道
    }

    public static void send(Channel channel) {
        final String textMsg = "握手完成后发送的消息"; // 要发送的消息内容

        if (channel != null && channel.isActive()) {
            TextWebSocketFrame frame = new TextWebSocketFrame(textMsg); // 创建 WebSocket 文本帧
            channel.writeAndFlush(frame).addListener((ChannelFutureListener) channelFuture -> {
                if (channelFuture.isDone() && channelFuture.isSuccess()) {
                    System.out.println("     ================= 发送成功.");
                } else {
                    channelFuture.channel().close();
                    System.out.println("     ================= 发送失败. cause = " + channelFuture.cause());
                    channelFuture.cause().printStackTrace();
                }
            });
        } else {
            System.out.println("消息发送失败！ textMsg = " + textMsg);
        }
    }


    // WebSocket客户端处理器
    public static class WebSocketClientHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {


        // 当从服务器接收到消息时调用
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) {
            System.out.println(" 客户端收到消息======== " + msg.text());

        }

        @Override
        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
            if (WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_COMPLETE.equals(evt)) {
                System.out.println(ctx.channel().id().asShortText() + " 握手完成！");
                latch.countDown(); // 计数减一，握手完成
                send(ctx.channel()); // 发送消息
            }
            super.userEventTriggered(ctx, evt);
        }


        // 当通道不活动时调用
        @Override
        public void channelInactive(ChannelHandlerContext ctx) {
            System.out.println("channelInactive");
        }

    }
}
